package com.yf.ref;

import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;

public class PhantomReferenceTest {
    public static PhantomReferenceTest obj; // 当前类对象
    static ReferenceQueue<PhantomReferenceTest> rq = null; //引用对列

    public static void main(String[] args) {
        PhantomRefQueue prq = new PhantomRefQueue();
        // 设置为守护线程（当程序中没有非守护线程时，守护线程也就结束了）
        // 此例中，当主线程结束了，CheckRefQueue线程也就跟着结束了
        prq.setDaemon(true);
        prq.start();
        /*
        虚引用在创建时必须提供一个引用队列作为参数。
        当垃圾回收器准备回收一个对象时，如果发现它还有虚引用，
        就会在回收对象后，将这个虚引用加入引用队列，以通知应用程序对象的回收情况。
         */
        rq = new ReferenceQueue<>();
        obj = new PhantomReferenceTest();
        PhantomReference<PhantomReferenceTest> ref = new PhantomReference<>(obj, rq);

        try {
            System.out.println("通过虚引用获取对象：" + ref.get());
            System.out.println("第1次gc");
            // 将强引用去除
            obj = null;
            // 第一次gc, 对象在finalize方法中被复活
            System.gc();
            Thread.sleep(1000);
            if (obj == null) {
                System.out.println("obj 为null");
            } else {
                System.out.println("obj 可用");
            }
            System.out.println("第2次gc");
            obj = null;
            // 第二次gc, finalize方法只能被gc回收器执行一次，obj无法复活
            // 一旦obj被回收，就会将此虚引用存放到引用队列中
            System.gc();
            Thread.sleep(1000);
            if (obj == null) {
                System.out.println("obj 为null");
            } else {
                System.out.println("obj 可用");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // finalize方法只会被调用一次
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("调用当前类的finalize方法");
        obj = this;
    }

    public static class PhantomRefQueue extends Thread {
        @Override
        public void run() {
            while (true) {
                if (rq != null) {
                    PhantomReference<PhantomReferenceTest> pf = null;
                    try {
                        pf = (PhantomReference<PhantomReferenceTest>) rq.remove();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (pf != null) {
                        System.out.println("追踪垃圾回收过程：PhantomReferenceTest实例被GC了");
                    }
                }
            }
        }
    }

}
